home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-08-19 | 9.6 KB | 336 lines | [TEXT/CWIE] |
- /*************************************************************************************
- #
- # Command.cp
- #
- # The command section accomplishes two things for the game -- first, providing a
- # standard clock to which all actions can be synchonized, and second, grabbing the state
- # of the keyboard to pass along to those game parts that need it.
- #
- # This code implements a VBL take that goes out and grabs the state of the keyboard once
- # per frame of animation. To minimize the hit, all this does is save this command state
- # off into a buffer and then exit.
- #
- # We create a buffer for two seconds worth of commands, although in practice we want to
- # pull commands from the buffer much faster than this.
- #
- # Eventually, this code will also probably need to deal with networking tasks.
- #
- # Author: Timothy Carroll
- # Apple Developer Technical Support
- # timc@apple.com
- #
- # Modification History:
- #
- # 8/15/96 TMC Initial Release
- #
- # Copyright © 1996 Apple Computer, Inc., All Rights Reserved
- #
- #
- # You may incorporate this sample code into your applications without
- # restriction, though the sample code has been provided "AS IS" and the
- # responsibility for its operation is 100% yours. However, what you are
- # not permitted to do is to redistribute the source as "DSC Sample Code"
- # after having made changes. If you're going to re-distribute the source,
- # we require that you make it clear in the source that the code was
- # descended from Apple Sample Code, but that you've made changes.
- #
- *************************************************************************************/
-
-
- /******************************************************************************************
- Other notes:
-
- 1. The 68K version of the code must store an A5 context in an expanded VBL record so that
- the VBL task can call up information. The PowerPC System Software defines a slightly
- different version of the record and uses standard fragment RTOCs to get at the application
- context.
-
- 2. The data buffer must be locked and a fixed size to prevent memory from being moved around
- in the VBL task.
-
- 3. We create the task in the application heap so that it will be cleared automatically if the
- app dies. If we change it to a system level task, then we need to patch ExitToShell to make
- sure we dispose of it before exiting the app.
-
- ******************************************************************************************/
-
- #include <Retrace.h>
- #include <Errors.h>
-
- #include "Command.h"
-
-
- // First, the record that will hold our running VBL task.
-
- typedef struct CommandVBLRec *CommandVBLRecPtr;
-
- #if GENERATING68K
- // The VBL task structure
- struct CommandVBLRec {
- VBLTask myVBLTask;
- long vblA5;
- Boolean running;
- };
- // and the VBL routines -- GetVBLRec is used to retrieve our record, since a 68K task passes
- // the data in via a register rather than on the stack.
- CommandVBLRecPtr GetVBLRec (void) = 0x2008; /*MOVE.L A0,D0*/
- void CommandVBL (void);
-
- #else
- // The VBL task structure
- struct CommandVBLRec {
- VBLTask myVBLTask;
- Boolean running;
- };
-
- // and the VBL routine
- void CommandVBL (VBLTaskPtr recPtr);
-
- #endif
-
-
-
- // Need to define a UPP for the command.
- static VBLUPP CommandVBLUPP = NewVBLProc (CommandVBL);
-
-
-
-
- // these variables are set by the frames desired passed in by the game.
- static short sFramesDesired = 0;
- static short sSizeOfBuffer = 0;
- static TCommand *sCommandBuffer = NULL;
-
- // These hold the locations that we'll store and retrieve new commands from.
- static short sInCommandIndex = 0;
- static short sOutCommandIndex = 0;
-
- // Finally, the data structure used by the VBL itself. I suppose we could add all of
- // our elements to the CommandVBLRec, which would allow us multiple command tasks. Why
- // would we need to though?
-
- static CommandVBLRec sCommandVBL;
-
-
-
- /******************************************************************************************
- InitializeCommands
-
- This routine fills in and allocates all structures required for a new command task.
- It returns an error if you try calling InitializeCommands twice in a row, or if it
- can't allocate the memory it needs to run the task.
-
- ******************************************************************************************/
-
- OSErr InitializeCommands (short framesDesired)
- {
- OSErr theErr = noErr;
-
- if (sCommandBuffer != NULL)
- return paramErr;
-
- // Allocate the command buffer. 2 seconds worth of room in the buffer should be enough
- sFramesDesired = framesDesired;
- sSizeOfBuffer = 120 / framesDesired;
- sInCommandIndex = 0;
- sOutCommandIndex = 0;
-
- sCommandBuffer = (TCommand *) NewPtr (sizeof (TCommand) * sSizeOfBuffer);
-
- theErr = MemError();
-
- FAIL_OSERR (theErr, "\pUnable to allocate the command buffer")
- FAIL_NIL (sCommandBuffer, "\pUnable to allocate the command buffer")
-
-
- // create and install the VBL task.
-
- sCommandVBL.myVBLTask.qType = vType;
- sCommandVBL.myVBLTask.vblAddr = CommandVBLUPP;
- sCommandVBL.myVBLTask.vblCount = 0; // we'll turn it on when we start accepting commands.
- sCommandVBL.myVBLTask.vblPhase = 0;
- sCommandVBL.running = false;
-
- #if GENERATING68K
- sCommandVBL.vblA5 = GetCurrentA5();
- #endif
-
- theErr = VInstall((QElemPtr) &sCommandVBL);
-
- FAIL_OSERR (theErr, "\pUnable to install the VBL task")
-
- return noErr;
-
- error:
-
- if (sCommandBuffer != NULL)
- DisposePtr ((Ptr)sCommandBuffer);
- sCommandBuffer = NULL;
-
- if (theErr == noErr)
- theErr = paramErr;
- return theErr;
- }
-
- /******************************************************************************************
- ExitCommands
-
- Clean up all the stuff we did when we built this task.
-
- ******************************************************************************************/
-
-
- OSErr ExitCommands (void)
- {
- // We'll stop this task if it hasn't been already.
- StopAcceptingCommands();
-
- DisposePtr ((Ptr) sCommandBuffer);
- sCommandBuffer = NULL;
- // remove the VBL task and throw it away.
- return VRemove((QElemPtr) &sCommandVBL);
-
- }
-
- /******************************************************************************************
- FlushCommandBuffer
-
- This empties any existing commands in the buffer. Only call it when the task is not
- running.
-
- ******************************************************************************************/
-
- void FlushCommandBuffer (void)
- {
- #if qDebugging
- if (sCommandVBL.running)
- // Need to make sure the VBL task is stopped when we do this.
- DebugStr("\pCalled FlushCommandBuffer while task was running!");
- #endif
-
- sInCommandIndex = sOutCommandIndex = 0;
- }
-
-
-
- /******************************************************************************************
- StartAcceptingCommands
-
- ******************************************************************************************/
-
-
- void StartAcceptingCommands (void)
- {
- #if qDebugging
- if (sCommandVBL.running)
- DebugStr("\pCalled StartAcceptingCommands on an already running task!");
- #endif
-
- // start up the VBL task!
- sCommandVBL.myVBLTask.vblCount = sFramesDesired;
- sCommandVBL.running = true;
- }
-
-
- /******************************************************************************************
- StopAcceptingCommands
-
- ******************************************************************************************/
-
- void StopAcceptingCommands (void)
- {
- // stop the VBL task
- sCommandVBL.myVBLTask.vblCount = 0;
- sCommandVBL.running = false;
- }
-
-
-
- /******************************************************************************************
- IsCommandWaiting
-
- ******************************************************************************************/
- Boolean IsCommandWaiting (void)
- {
- if (sInCommandIndex != sOutCommandIndex)
- return true;
- else
- return false;
- }
-
-
-
- /******************************************************************************************
- RetrieveCommand
-
- Retrieves the next command from the buffer, returning false if there actually isn't a
- command available.
-
- ******************************************************************************************/
- Boolean RetrieveCommand (TCommand *outCommand)
- {
- if (sInCommandIndex != sOutCommandIndex)
- {
- sOutCommandIndex = (sOutCommandIndex+1) % sSizeOfBuffer;
- *outCommand = sCommandBuffer[sOutCommandIndex];
- return true;
- }
- else
- return false;
-
- }
-
-
-
-
- // Now for the task itself. The code is a little ugly because of the conditionalized
- // stuff for 68K. Basically we set up the task, do our magic and then tear everything down.
-
- #if GENERATING68K
- // On classic 68K macs, we need to retrieve our stored A5 and get the pointer to our VBL
- // task record.
- void CommandVBL (void)
- {
- CommandVBLRecPtr recPtr = GetVBLRec(); /*pointer to task record*/
- long curA5 = SetA5(recPtr->vblA5); /*stored value of A5*/
-
- #else
- // On powermacs, we can just open up the task and go!
- void CommandVBL (VBLTaskPtr recPtr)
- {
- #endif
-
-
-
- // Finally we actually can do our work.
-
- sInCommandIndex = (sInCommandIndex+1) % sSizeOfBuffer;
-
- // If InCommandIndex is equal to the OutCommandIndex, it means
- // we've overlapped completely and we should throw out the new
- // command, so we throw out the new command rather than overwriting it.
- // Ideally, the game engine should be fast enough that this should never happen.
-
- if (sInCommandIndex != sOutCommandIndex)
- {
- // Get the keys and stuff them into the next available slot.
- // We're actually using the variant array syntax in C here.
- GetKeys((sCommandBuffer[sInCommandIndex]).keys);
- }
- else
- {
- // After decrementing, sInCommandIndex could conceivably be -1. But if you
- // look above, we always add one, so we won't ever use the negative address.
- sInCommandIndex--;
- }
-
- // Reset vblCount so that this procedure executes again.
- // And of course, on a 68K mac we need to clean up after ourselves.
- #if GENERATING68K
- recPtr->myVBLTask.vblCount = sFramesDesired;
- (void) SetA5(curA5);
- #else
- recPtr->vblCount = sFramesDesired;
- #endif
-
- }